home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Frameworks / Hsoi's App Shell 1.0a4 / HAS Other Source / WASTE 1.3a4 Distribution / WASTE 1.3a4 / Source / WELineLayout.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-04-04  |  14.7 KB  |  531 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WELineLayout.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Line Layout, Getting and Setting Variables, etc.
  6.  *
  7.  *  Copyright (c) 1993-1997 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. INLINE pascal void _WERemoveLine(SInt32 lineIndex, WEPtr pWE)
  18. {
  19.     // remove the line
  20.     _WESplice((Handle) pWE->hLines, nil, - sizeof(WELineRec), lineIndex * sizeof(WELineRec));
  21.  
  22.     // decrement line count
  23.     pWE->nLines--;
  24. }
  25.  
  26. pascal OSErr _WEInsertLine(SInt32 lineIndex, const WELineRec *pLine, WEPtr pWE)
  27. {
  28.     // insert the specified entry in the line array
  29.     OSErr err;
  30.  
  31.     // do the insertion
  32.     if ((err = _WESplice((Handle) pWE->hLines, pLine, sizeof(WELineRec), lineIndex * sizeof(WELineRec))) != noErr)
  33.     {
  34.         return err;
  35.     }
  36.  
  37.     // increment line count
  38.     pWE->nLines++;
  39.     return noErr;
  40. }
  41.  
  42. pascal void _WEBumpOrigin(SInt32 lineIndex, SInt32 deltaOrigin, WEPtr pWE)
  43. {
  44.     WELineRec *pLine = *pWE->hLines + lineIndex;
  45.     SInt32 nLines = pWE->nLines;
  46.  
  47.     // loop through the line run array adjusting the lineOrigin fields
  48.     for ( ; lineIndex <= nLines; lineIndex++ )
  49.     {
  50.         pLine->lineOrigin += deltaOrigin;
  51.         pLine++;
  52.     }
  53. }
  54.  
  55. pascal SInt32 _WEFindLineBreak(SInt32 lineStart, WEHandle hWE)
  56. {
  57.     // Find where to break the line beginning at lineStart
  58.     // the current graphics port must be already set up correctly
  59.  
  60.     WEPtr pWE = *hWE;    // assume WE record is already locked
  61.     Ptr pText;
  62.     SInt32 offset, breakOffset;
  63.     SInt32 textLength;
  64.     SInt32 remainingLength;
  65.     SInt32 segmentStart, segmentEnd;
  66.     SInt32 runIndex;
  67.     WERunInfo runInfo;
  68.     Fixed pixelWidth;
  69.     ScriptCode script, previousScript;
  70.     Boolean isBreak = false;
  71.  
  72.     offset = lineStart;
  73.     pText = *pWE->hText + offset;
  74.     remainingLength = pWE->textLength - offset;
  75.  
  76.     // find the style run index corresponding to the first segment on this line
  77.     runIndex = WEOffsetToRun(offset, hWE);
  78.  
  79.     // initialize pixelWidth to the width of the destination rectangle, as a Fixed quantity
  80.     pixelWidth = BSL((pWE->destRect.right - pWE->destRect.left), 16);
  81.  
  82.     // STYLE SEGMENT LOOP
  83.     do
  84.     {
  85.  
  86.         // get style run information for the current style run
  87.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  88.         runIndex++;
  89.  
  90.         // set text attributes in the graphics port
  91.         TextFont(runInfo.runAttrs.runStyle.tsFont);
  92.         TextFace(runInfo.runAttrs.runStyle.tsFace);
  93.         TextSize(runInfo.runAttrs.runStyle.tsSize);
  94.  
  95.         // if we're handling multiscript text, keep track of script boundaries
  96.         if (BTST(pWE->flags, weFNonRoman))
  97.         {
  98.             // what is the script for this segment?
  99.             script = FontToScript(runInfo.runAttrs.runStyle.tsFont);
  100.  
  101.             // have we crossed a script run boundary in the middle of a line?
  102.             if ((runInfo.runStart > offset) && (script != previousScript))
  103.             {
  104.                 // leave behind the all previous segments on this line
  105.                 offset = runInfo.runStart;
  106.                 pText = *pWE->hText + offset;
  107.                 remainingLength = pWE->textLength - offset;
  108.             }
  109.             previousScript = script;
  110.         } // if non-Roman
  111.  
  112.         // we'll pass textLength as the second parameter to the line break hook
  113.         // although this parameter is declared as a long, StyledLineBreak uses only
  114.         // the low word, so make sure it doesn't trespass the 32,767 byte threshold!
  115.         textLength = _WEPinInRange(remainingLength, 0, SHRT_MAX);
  116.  
  117.         // calculate segmentStart and segmentEnd relative to offset
  118.         segmentStart = _WEPinInRange(runInfo.runStart - offset, 0, textLength);
  119.         segmentEnd = _WEPinInRange(runInfo.runEnd - offset, 0, textLength);
  120.  
  121.         // set breakOffset to a non-zero value for the first script run on the line,
  122.         // set it to zero for all subsequent script runs
  123.         breakOffset = (offset == lineStart);
  124.  
  125. #if WASTE_OBJECTS
  126.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  127.         {
  128.             // EMBEDDED OBJECT
  129.             // subtract object width from pixelWidth
  130.             pixelWidth -= BSL((*runInfo.runAttrs.runStyle.tsObject)->objectSize.h, 16);
  131.  
  132.             // stop looping if pixelWidth has gone negative
  133.             isBreak = (pixelWidth < 0);
  134.             breakOffset = isBreak ? segmentStart : segmentEnd;
  135.         }
  136.         else
  137. #endif
  138.         {
  139.             // REGULAR TEXT
  140.             isBreak = (CallWELineBreakProc(pText, textLength, segmentStart, segmentEnd,
  141.                 &pixelWidth, &breakOffset, hWE, pWE->lineBreakHook) != smBreakOverflow);
  142.         }
  143.  
  144.         // break the line anyway when we reach the end of the text
  145.         if (segmentEnd >= remainingLength)
  146.         {
  147.             isBreak = true;
  148.         }
  149.     } while (!isBreak);
  150.  
  151.     // return the offset from lineStart to the break point
  152.     return (offset - lineStart) + breakOffset;
  153. }
  154.  
  155. pascal void _WECalcHeights(SInt32 rangeStart, SInt32 rangeEnd, SInt16 *lineAscent, SInt16 *lineDescent,
  156.         WEHandle hWE)
  157. {
  158.     // Find the maximum ascent and descent values between rangeStart and rangeEnd
  159.     // the WE record must be already locked
  160.     // the current graphics port must be already set up correctly
  161.  
  162.     SInt32 runIndex;
  163.     WERunInfo runInfo;
  164.     SInt16 runAscent, runDescent;
  165.  
  166.     *lineAscent = 1;
  167.     *lineDescent = 0;
  168.  
  169.     // find the style run index corresponding to the first segment on this line
  170.     runIndex = WEOffsetToRun(rangeStart, hWE);
  171.  
  172.     // STYLE SEGMENT LOOP
  173.     do
  174.     {
  175.         // get style run information for the current style run
  176.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  177.         runIndex++;
  178.  
  179.         // calculate ascent and descent (actually, descent + leading) values for this style run
  180.  
  181. #if WASTE_OBJECTS
  182.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  183.         {
  184.             // EMBEDDED OBJECT
  185.             runAscent = (*runInfo.runAttrs.runStyle.tsObject)->objectSize.v;
  186.             runDescent = 0;
  187.         }
  188.         else
  189. #endif
  190.         {
  191.             // REGULAR TEXT
  192.             runAscent = runInfo.runAttrs.runAscent;
  193.             runDescent = runInfo.runAttrs.runHeight - runAscent;
  194.         }
  195.  
  196.         // save the maximum values in lineAscent and lineDescent
  197.         if (runAscent > *lineAscent)
  198.         {
  199.             *lineAscent = runAscent;
  200.         }
  201.  
  202.         if (runDescent > *lineDescent)
  203.         {
  204.             *lineDescent = runDescent;
  205.         }
  206.  
  207.         // keep looping until we reach rangeEnd
  208.     } while (runInfo.runEnd < rangeEnd);
  209. }
  210.  
  211. pascal OSErr _WERecalBreaks(SInt32 *startLine, SInt32 *endLine, WEHandle hWE)
  212. {
  213.     // Recalculates line breaks, line heights and ascents for all the text or for a portion of it.
  214.     // On entry, startLine and endLine define a range of lines to recalculate.
  215.     // On exit, startLine to endLine defines the range of lines actually recalculated
  216.     // the WE record must already be locked
  217.  
  218.     WEPtr pWE = *hWE;
  219.     WELineRec *pLine;
  220.     WELineRec lineInfo, oldLineInfo;
  221.     SInt32 lineIndex;
  222.     SInt32 recalThreshold;
  223.     SInt32 lineOffset;
  224.     SInt16 lineAscent, lineDescent;
  225.     SInt32 textHeight;
  226.     Boolean saveTextLock;
  227.     QDEnvironment saveEnvironment;
  228.     OSErr err = noErr;
  229.  
  230.     // lock the text
  231.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  232.  
  233.     // find the character offset that must be necessarily reached before we can
  234.     // even consider the possibility of stopping the recalculation process
  235.     // this offset, recalThreshold, is the last character on endLine _before_ recalculation
  236.     lineIndex = _WEPinInRange(*endLine, 0, pWE->nLines - 1);
  237.     recalThreshold = (*pWE->hLines)[lineIndex + 1].lineStart;
  238.  
  239.     // we start recalculating line breaks from the line actually _preceding_ startLine,
  240.     // since editing startLine may cause part of its text to fit on the preceding line
  241.     lineIndex = _WEPinInRange(*startLine - 1, 0, pWE->nLines - 1);
  242.  
  243.     // find where in the text recalculation should begin
  244.     lineInfo = (*pWE->hLines)[lineIndex];
  245.  
  246.     // save the Quickdraw environment
  247.     _WESaveQDEnvironment(pWE->port, false, &saveEnvironment);
  248.  
  249.     // MAIN LINE BREAKING LOOP
  250.     do
  251.     {
  252.         // find where to break the current line
  253.         lineOffset = _WEFindLineBreak(lineInfo.lineStart, hWE);
  254.  
  255.         // make sure we advance at least by one character (unless we reached the end of text)
  256.         if ((lineOffset <= 0) && (lineInfo.lineStart < pWE->textLength))
  257.         {
  258.             lineOffset = 1;
  259.         }
  260.  
  261.         // calculate ascent and descent values for this line
  262.         _WECalcHeights(lineInfo.lineStart, lineInfo.lineStart + lineOffset, &lineAscent, &lineDescent, hWE);
  263.  
  264.         // save the maximum line ascent for this line in the line array
  265.         pLine = *pWE->hLines + lineIndex;
  266.         pLine->lineAscent = lineAscent;
  267.  
  268.         // increment counters (go to the next line array entry)
  269.         lineIndex++;
  270.         lineInfo.lineStart += lineOffset;
  271.         lineInfo.lineOrigin += (lineAscent + lineDescent);
  272.         pLine++;
  273.  
  274.         // compare the newly calculated line start with the old value
  275.         // if the new line start comes before the old line start, insert a new element
  276.         oldLineInfo = *pLine;
  277.         if ((lineIndex > pWE->nLines) || (lineInfo.lineStart < oldLineInfo.lineStart))
  278.         {
  279.             if ((err = _WEInsertLine(lineIndex, &lineInfo, pWE)) != noErr)
  280.             {
  281.                 goto cleanup;
  282.             }
  283.         }
  284.         else
  285.         {
  286.             // overwrite the old element
  287.             pLine->lineStart = lineInfo.lineStart;
  288.             pLine->lineOrigin = lineInfo.lineOrigin;
  289.  
  290.             // remove all further elements which have a lineStart field
  291.             // less than or equal to the current one
  292.             while((lineIndex < pWE->nLines) && (lineInfo.lineStart >= (pLine + 1)->lineStart))
  293.                 _WERemoveLine(lineIndex + 1, pWE);
  294.  
  295.             // if the new line start is the same as the old one...
  296.             if (lineInfo.lineStart == oldLineInfo.lineStart)
  297.             {
  298.                 // ...and recalThreshold has been reached, we can stop recalculating line breaks
  299.                 if (lineInfo.lineStart >= recalThreshold)
  300.                 {
  301.                     // although line breaks need not be changed from lineIndex on,
  302.                     // the lineOrigin fields may need to be changed
  303.                     if (lineInfo.lineOrigin != oldLineInfo.lineOrigin)
  304.                     {
  305.                         _WEBumpOrigin(lineIndex + 1, lineInfo.lineOrigin - oldLineInfo.lineOrigin, pWE);
  306.                     }
  307.  
  308.                     // exit from the line breaking loop
  309.                     goto cleanup;
  310.                 }
  311.             }
  312.             else
  313.             {
  314.                 // otherwise, the new line start comes after the old line start...
  315.                 // if the current line is the one preceding startLine, warn our caller about this
  316.                 if ((lineIndex > 0) && (lineIndex == *startLine))
  317.                 {
  318.                     *startLine = lineIndex - 1;
  319.                 }
  320.             }
  321.         }
  322.     } while(lineInfo.lineStart < pWE->textLength);
  323.  
  324. cleanup:
  325.     // calculate total text height
  326.     textHeight = WEGetHeight(0, pWE->nLines, hWE);
  327.  
  328.     // quirk: if the last character in the text is a carriage return, the caret appears
  329.     // below the last line, so in this case we need to add the extra height to textHeight
  330.     if (WEGetChar(pWE->textLength - 1, hWE) == kEOL)
  331.     {
  332.         textHeight += WEGetHeight(pWE->nLines - 1, pWE->nLines, hWE);
  333.     }
  334.  
  335.     // if total text height has changed, remember to call the scroll callback later
  336.     if (textHeight != pWE->destRect.bottom - pWE->destRect.top)
  337.     {
  338.         BSET(pWE->flags, weFDestRectChanged);
  339.     }
  340.  
  341.     // set destRect.bottom to destRect.top + total text height
  342.     pWE->destRect.bottom = pWE->destRect.top + textHeight;
  343.  
  344.     // return through endLine the index of the last line affected by recalculation
  345.     *endLine = lineIndex - 1;
  346.  
  347.     // make sure startLine isn't greater than endLine
  348.     if (*startLine > *endLine)
  349.     {
  350.         *startLine = *endLine;
  351.     }
  352.  
  353.     // unlock the text
  354.     _WESetHandleLock(pWE->hText, saveTextLock);
  355.  
  356.     // restore the Quickdraw environment
  357.     _WERestoreQDEnvironment(&saveEnvironment);
  358.  
  359.     // return result code
  360.     return err;
  361. }
  362.  
  363. static Boolean SLCalcSlop
  364.     (
  365.         WELineRec *pLine,
  366.         const WERunAttributes *pAttrs,
  367.         Ptr pSegment,
  368.         SInt32 segmentStart,
  369.         SInt32 segmentLength,
  370.         JustStyleCode styleRunPosition,
  371.         WEHandle hWE,
  372.         void *callbackData
  373.     )
  374. {
  375.     struct SLCalcSlopData *cd = (struct SLCalcSlopData *) callbackData;
  376.     SInt16 segmentWidth;
  377.     Fixed segmentProportion;
  378.     Boolean isEndOfLine;
  379.  
  380.     // see if this text segment ends with a carriage return, or if we've reached the
  381.     // end of the text (in which case we don't want any justification to take place)
  382.     isEndOfLine = (segmentStart + segmentLength >= (*hWE)->textLength) ||
  383.                   (pSegment [ segmentLength - 1 ] == kEOL);
  384.  
  385.     // if this is the first segment on the line, reset line totals
  386.     if (IS_LEFTMOST_RUN(styleRunPosition))
  387.     {
  388.         cd->totalSlop = cd->lineWidth;
  389.         cd->totalProportion = 0;
  390.     }
  391.  
  392. #if WASTE_OBJECTS
  393.     if (pAttrs->runStyle.tsObject != nil)
  394.     {
  395.         // EMBEDDED OBJECT
  396.         // segment width is just object width; no extra space can be applied for justification
  397.         segmentWidth = (*pAttrs->runStyle.tsObject)->objectSize.h;
  398.         segmentProportion = 0;
  399.     }
  400.     else
  401. #endif
  402.     {
  403.         // REGULAR TEXT
  404.  
  405.         // if this is the last segment on the line, strip trailing spaces
  406.         if (IS_RIGHTMOST_RUN(styleRunPosition))
  407.         {
  408.             segmentLength = VisibleLength(pSegment, segmentLength);
  409.         }
  410.  
  411.         // measure this segment
  412.         segmentWidth = TextWidth(pSegment, 0, segmentLength);
  413.  
  414.         // calculate the proportion of extra space to apply to this text segment
  415.         segmentProportion = PortionLine(pSegment, segmentLength,
  416.                 styleRunPosition, kOneToOneScaling, kOneToOneScaling);
  417.     }
  418.  
  419.     // keep track of line totals
  420.     cd->totalSlop -= segmentWidth;
  421.     cd->totalProportion += segmentProportion;
  422.  
  423.     // if this is the last segment on the line, save values in the line array
  424.     if (IS_RIGHTMOST_RUN(styleRunPosition))
  425.     {
  426.         // make sure slop is non-negative
  427.         if (cd->totalSlop < 0)
  428.         {
  429.             cd->totalSlop = 0;
  430.         }
  431.         pLine->lineSlop = cd->totalSlop;
  432.         pLine->lineJustAmount = isEndOfLine ? 0 : FixDiv(BSL(cd->totalSlop, 16), cd->totalProportion);
  433.     }
  434.     return false;    // keep looping
  435. }
  436.  
  437. pascal void _WERecalSlops(SInt32 firstLine, SInt32 lastLine, WEHandle hWE)
  438. {
  439.     // Calculates the lineSlop and lineJustAmount fields
  440.     // of the line array for the specified lines
  441.  
  442.     WEPtr pWE = *hWE;
  443.     struct SLCalcSlopData cd;
  444.  
  445.     // we only need to bother if the user isn't using left justification
  446.     if (pWE->alignment == weFlushLeft)
  447.     {
  448.         return;
  449.     }
  450.  
  451.     // calculate slop and normalized slop proportion for all lines
  452.     cd.lineWidth = pWE->destRect.right - pWE->destRect.left;
  453.     _WESegmentLoop(firstLine, lastLine, SLCalcSlop, &cd, hWE);
  454. }
  455.  
  456. pascal void WESetAlignment(WEAlignment alignment, WEHandle hWE)
  457. {
  458.     WEPtr pWE;
  459.     WEAlignment oldAlignment;
  460.     Boolean saveWELock;
  461.  
  462.     // lock the WE record
  463.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  464.     pWE = *hWE;
  465.     oldAlignment = pWE->alignment;
  466.  
  467.     if ((alignment >= weFlushLeft) && (alignment <= weJustify) && (alignment != oldAlignment))
  468.     {
  469.         // hide the caret if it's showing
  470.         if (BTST(pWE->flags, weFCaretVisible))
  471.         {
  472.             _WEBlinkCaret(hWE);
  473.         }
  474.  
  475.         // change the alignment
  476.         pWE->alignment = alignment;
  477.  
  478.         if (! BTST(pWE->features, weFInhibitRecal))
  479.         {
  480.             // if the text was left-aligned, then we haven't been bothering till now,
  481.             // so we have to recalc the whole document
  482.             if (oldAlignment == weFlushLeft)
  483.             {
  484.                 _WERecalSlops(0, pWE->nLines - 1, hWE);
  485.             }
  486.  
  487.             if (! BTST(pWE->features, weFInhibitRedraw))
  488.             {
  489.                 // redraw the view rectangle
  490.                 WEUpdate(nil, hWE);
  491.             }
  492.         }
  493.     }
  494.  
  495.     // unlock the WE record
  496.     _WESetHandleLock((Handle) hWE, saveWELock);
  497. }
  498.  
  499. pascal void WESetDirection(WEDirection direction, WEHandle hWE)
  500. {
  501.     WEPtr pWE;
  502.     WEDirection oldDirection;
  503.     Boolean saveWELock;
  504.  
  505.     // lock the WE record
  506.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  507.     pWE = *hWE;
  508.     oldDirection = pWE->direction;
  509.  
  510.     if ((direction >= weDirRightToLeft) && (direction <= weDirDefault) && (direction != oldDirection))
  511.     {
  512.         // hide the caret if it's showing
  513.         if (BTST(pWE->flags, weFCaretVisible))
  514.         {
  515.             _WEBlinkCaret(hWE);
  516.         }
  517.  
  518.         // change the direction
  519.         pWE->direction = direction;
  520.  
  521.         if (! (pWE->features & ((1L << weFInhibitRecal) | (1L << weFInhibitRedraw))))
  522.         {
  523.             // redraw the view rectangle
  524.             WEUpdate(nil, hWE);
  525.         }
  526.     }
  527.  
  528.     // unlock the WE record
  529.     _WESetHandleLock((Handle) hWE, saveWELock);
  530. }
  531.